//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler execution subsystem
// FILE:            XortJit64.cpp - JIT code generator for x64 bit architecture
// AUTHOR:          Vladimir Gumenuk
// DATE:            7-Oct-2014
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"
#include "XortJit64.h"
#include "xgen.h"

#define IDX2DISP(x) (x*8)

XortJit64::XortJit64()
{
    m_pGlobalVars = NULL;
    m_pDATA = NULL;
    m_pCODE = NULL;
    m_pGenerator = NULL;
    m_dwInstrIdx = 0;
    m_pCurProc = NULL;
}

XortJit64::~XortJit64()
{
    if( m_pGenerator )
    {
        delete m_pGenerator;
        m_pGenerator = NULL;
    }

    if( m_pDATA )
    {
        _aligned_free( m_pDATA );
        m_pDATA = NULL;
    }

    if( m_pCODE )
    {
        _aligned_free( m_pCODE );
        m_pCODE = NULL;
    }
}

// Create and initialize global data block
int XortJit64::CreateDataBlock( XCELLSARRAY& globalVars )
{
    if( m_pDATA )
        return ERR_INTERNAL;

    dword dwBlockSize = 0;
    dword dwNumVars = globalVars.count();
    if( dwNumVars == 0 )
        return XORT_OK; // excellent programmer - no global variables :)

    m_pGlobalVars = &globalVars;

    for( dword i = 0; i < dwNumVars; i++ ) // calculate the size of global data block
    {
        XortElement& var = globalVars.get( i );
        switch( var.bType )
        {
        case t_v:  return ERR_INVALID_TYPE;
        case t_s1:
        case t_u1:
            dwBlockSize += __alignof( char ) + CHAR_ALIGN;
            break;
        case t_s2:
        case t_u2:
            dwBlockSize += __alignof( short ) + SHORT_ALIGN;
            break;
        case t_s4:
        case t_u4:
            dwBlockSize += __alignof( int );
            break;
        case t_s8:
        case t_u8:
            dwBlockSize += __alignof( __int64 );
            break;
        case t_r4:
            dwBlockSize += __alignof( float );
            break;
        case t_r8:
            dwBlockSize += __alignof( double );
            break;
        case t_p :
            dwBlockSize += __alignof( void* );
            break;
        }
    }

    m_pDATA = _aligned_malloc( dwBlockSize, X64_ALLIGNMENT ); // allocate aligned to 16 byte data block
    if( !m_pDATA )
        return ERR_MEMORY_ALLOCATION;

    qword dwStartAddr = (qword)m_pDATA;
    byte *pDATA = (byte *)m_pDATA;

    dwBlockSize = 0;

    for( dword i = 0; i < dwNumVars; i++ ) // go again and calculate the pointer to each variable
    {
        qword qwVarAddr = dwStartAddr + dwBlockSize;
        m_dataPointers.add( qwVarAddr );

        XortElement& var = globalVars.get( i );
        switch( var.bType )
        {
        case t_v:  return ERR_INVALID_TYPE;
        case t_s1:
        case t_u1:
            {
                byte *p = (byte *)&pDATA[dwBlockSize];
                *p = var._u1;
                dwBlockSize += __alignof(char)+CHAR_ALIGN;
            }
            break;
        case t_s2:
        case t_u2:
            {
                word *p = (word *)&pDATA[dwBlockSize];
                *p = var._u2;
                dwBlockSize += __alignof(short)+SHORT_ALIGN;
            }
            break;
        case t_s4:
        case t_u4:
            {
                dword *p = (dword *)&pDATA[dwBlockSize];
                *p = var._u4;
                dwBlockSize += __alignof(int);
            }
            break;
        case t_s8:
        case t_u8:
            {
                qword *p = (qword *)&pDATA[dwBlockSize];
                *p = var._u8;
                dwBlockSize += __alignof(__int64);
            }
            break;
        case t_r4:
            {
                float *p = (float *)&pDATA[dwBlockSize];
                *p = var._r4;
                dwBlockSize += __alignof(float);
            }
            break;
        case t_r8:
            {
                double *p = (double *)&pDATA[dwBlockSize];
                *p = var._r8;
                dwBlockSize += __alignof(double);
            }
            break;
        case t_p:
            {
                qword *p = (qword *)&pDATA[dwBlockSize];
                *p = (qword)var._p;
                dwBlockSize += __alignof(void*);
            }
            break;
        }
    }

    return XORT_OK;
}

// TODO: this value should be experimentally selected!!!
// Try to predict the size of the future object code.
#define PROC_CODE_SIZE 10

// TODO: support object methods!
dword XortJit64::calcCodeSize( const SYMBOLSARRAY& globalFunc )
{
    dword dwCodeSize = 0;

    dword dwNumFunc = globalFunc.count();
    for( dword i = 0; i < dwNumFunc; i++ )
    {
        Symbol *pFunc = globalFunc.get( i );
        if( pFunc )
        {
            dwCodeSize += pFunc->m_Code.count() * PROC_CODE_SIZE;
        }
    }

    return dwCodeSize;
}

InstructionData* XortJit64::getNextInstruction()
{
    if( m_pCurProc && m_dwInstrIdx < m_pCurProc->m_Code.count() )
    {
        return m_pCurProc->m_Code.get( m_dwInstrIdx++ );
    }
    return NULL;
}

InstructionData* XortJit64::getCurInstruction()
{
    if( m_pCurProc && m_dwInstrIdx < m_pCurProc->m_Code.count() )
    {
        return m_pCurProc->m_Code.get( m_dwInstrIdx );
    }
    return NULL;
}

// xvm index: positive for locals, negative for function arguments
int XortJit64::xvmStackIdxToMachineIdx( int iXortIdx )
{
    int iStackIdx;
    int iBase;

    if( iXortIdx > 0 ) // local vars
    {
        iBase = m_Machine.getLocalStorageEnd();
    }
    else if( iXortIdx < 0 )
    {
        iBase = m_Machine.getStackBase();
    }
    else // == 0 ??
        return 0;

    iStackIdx = iBase + iXortIdx;

    return iStackIdx;
}

dword XortJit64::GetLocalVariablesCount( Symbol *pFunc )  // returns the number of local variables (stkvar based calculation)
{
    dword dwNumLocVars = 0;
    dword dwNumInstructions = pFunc->m_Code.count();
    dword dwIdx = 0;
    while( dwIdx < dwNumInstructions )
    {
        InstructionData *pInstrData = pFunc->m_Code.get( dwIdx );

        if( pInstrData->m_bInstrCode == p_stkvar )
            dwNumLocVars++;

        dwIdx++;
    }

    return dwNumLocVars;
}

int XortJit64::ProcessMethods( SYMBOLSARRAY& globalFunc ) // generate object code for each global function
{
    if( m_pCODE )
        return XORT_OK;

    int iRet = XORT_OK;

    dword dwCodeSize = calcCodeSize( globalFunc ) + globalFunc.count() * 8;
    if( dwCodeSize == 0 )
        return XORT_OK; // nothing to generate :)

    m_pCODE = _aligned_malloc( dwCodeSize, X64_ALLIGNMENT ); 
    if( !m_pCODE )
        return ERR_MEMORY_ALLOCATION;

    m_pGenerator = new XGenerator( m_pCODE, dwCodeSize );
    if( !m_pGenerator )
        return ERR_MEMORY_ALLOCATION;

    qword dwStartAddr = (qword)m_pCODE;
    byte *pCODE = (byte *)m_pCODE;

    dword dwProcCodeSize = 0;

    dword dwNumFunc = globalFunc.count();
    for( dword i = 0; i < dwNumFunc; i++ ) // go through each function and generate object code
    {
        qword qwProcAddr = dwStartAddr + dwProcCodeSize;

        m_funcPointers.add( qwProcAddr ); // save pointer to function

        Symbol *pFunc = globalFunc.get( i );
        if( pFunc )
        {
            dword dwSizeOfCode = 0;
            if( ( iRet = ProcessFunction( pFunc, dwSizeOfCode ) ) != XORT_OK )
                break;

            dwProcCodeSize += dwSizeOfCode;
        }
    }

    if( m_pGenerator )
    {
        m_pGenerator->ready();
        m_pGenerator->protect( m_pCODE, dwCodeSize, true );
        delete m_pGenerator;
        m_pGenerator = NULL;
    }

    typedef void _test_foo();
    _test_foo *pfn = (_test_foo*)m_pCODE;
    pfn();
    
    return iRet;
}

int XortJit64::ProcessFunction( Symbol *pFunc, dword& dwCodeSizeOut )
{
    int iRet;
    if( pFunc->m_dwFlags & f_buildinfunc ) // build in function
    {
        iRet = XORT_OK;
    }
    else if( pFunc->m_dwFlags & f_import_proc ) // imported function
    {
        iRet = XORT_OK;
    }
    else // XVM regular proc...
    {
        iRet = ProcessXortRegularProc( pFunc, dwCodeSizeOut );
    }

    return iRet;
}

int XortJit64::ProcessXortRegularProc( Symbol *pFunc, dword& dwCodeSizeOut )
{
    int iRet = XORT_OK;

    m_dwInstrIdx = 0;
    m_pCurProc = pFunc;
    dword dwNumLocalVars = GetLocalVariablesCount( pFunc );
    m_Machine.BuildArgumentsLayout( pFunc );
    gen_Prologue();
        
    dword dwNumInstructions = m_pCurProc->m_Code.count();

    while( getCurInstrIdx() < dwNumInstructions )
    {
        InstructionData PTR pInstr = getNextInstruction();

        switch( pInstr->m_bInstrCode )
        {
        case p_begswitch: iRet = do_begswitch(); break;
        case p_beginret:  iRet = do_beginret(); break;
        case p_beginasgn: iRet = do_beginasgn(); break;
        case p_begin_if:  iRet = do_begin_if(); break;
        case p_begincall: iRet = do_begincall(); break;
        case p_stkvar:    iRet = do_stkvar(); break;
        case p_label:     iRet = do_label( pInstr->m_Value._s4 ); break;
        default:{
                iRet = ProcessPrimitive( *pInstr );
                break;
            }
        }
    }

    //gen_Epilogue(); - generated in ret instruction

    return XORT_OK;
}

#define NUM_LOCAL_TEMP_VARS 8
void XortJit64::gen_Prologue()
{
    m_pGenerator->push_GPR( RBP );
    m_pGenerator->mov_GPR_GPR( RBP, RSP );
    m_pGenerator->sub_GPR_imm_byte( RSP, NUM_LOCAL_TEMP_VARS * sizeof(QWORD) );  // reserve stack for 8 local variables  

    // make changes to machine
    XortElement v; v.bType = t_u8; 
    m_Machine.Push( v );
    m_Machine.ReserveLocalStorage( NUM_LOCAL_TEMP_VARS );
}

void XortJit64::gen_Epilogue()
{
    m_Machine.RemoveLocalStorage( NUM_LOCAL_TEMP_VARS );
    m_Machine.Pop();
    m_pGenerator->add_GPR_imm_byte( RSP, NUM_LOCAL_TEMP_VARS * sizeof(QWORD) );
    m_pGenerator->pop_GPR( RBP );
    m_pGenerator->ret_Drop( 0 );
}

int XortJit64::ProcessMetaInstructions( Symbol *pFunc )
{
    int iRet = XORT_OK;
    return iRet;
}

int XortJit64::do_begswitch()
{
    int iRet = XORT_OK;
    return iRet;
}

int XortJit64::do_beginret()
{
    int iRet = XORT_OK;
    return iRet;
}

int XortJit64::do_beginasgn()
{
    int iRet = XORT_OK;

    InstructionData PTR pInstr = getNextInstruction();
    while( pInstr && iRet == XORT_OK )
    {
        switch( MEMBER_PTR(pInstr)m_bInstrCode )
        {
        case p_begincall: 
            iRet = do_begincall();
            break;

        case p_stkvar:
            iRet = do_stkvar();
            break;

        case p_begin_if:
            iRet = do_begin_if();
            break;

        default:
            while( pInstr && iRet == XORT_OK )
            {
                iRet = ProcessPrimitive( DEREF( pInstr ) );
                if( iRet == XORT_OK )
                {
                    pInstr = getNextInstruction();
                    if( pInstr && x_is_metainstruction( pInstr->m_bInstrCode) )
                    {
                        if( pInstr->m_bInstrCode == p_endasgn )
                            return XORT_OK; 
                        else
                            break; // continue to process meta instructions
                    }
                } else
                    return iRet;
            }
            break;
        }
    }

    return iRet;
}

int XortJit64::do_begin_if()
{
    int iRet = XORT_OK;
    return iRet;
}

int XortJit64::do_begincall()
{
    int iRet = XORT_OK;
    return iRet;
}

int XortJit64::do_stkvar()
{
    int iRet = XORT_OK;

    InstructionData PTR pInstr = getNextInstruction();
    while( pInstr && iRet == XORT_OK )
    {
        switch( MEMBER_PTR( pInstr )m_bInstrCode )
        {
        case p_begincall:
            iRet = do_begincall();
            break;

        default:
            while( pInstr && iRet == XORT_OK )
            {
                iRet = ProcessPrimitive( DEREF( pInstr ) );
                if( iRet == XORT_OK )
                {
                    pInstr = getNextInstruction();
                    if( pInstr && x_is_metainstruction( pInstr->m_bInstrCode ) )
                    {
                        if( pInstr->m_bInstrCode == p_endstkvar )
                        {
                            if( !m_Operands.canPop() ) return ERR_INTERNAL;

                            VarStorage st = m_Operands.pop();
                            iRet = gen_push_operand( st );
                            if( iRet == XORT_OK )
                            {
                                XortElement v = m_Machine.getFromStorage( st );
                                m_Machine.Push( v );
                                m_Machine.clearStorage( st );
                            }

                            return iRet;
                        }
                        else
                            break; // continue to process meta instructions
                    }
                }
                else
                    return iRet;
            }
            break;
        }
    }

    return iRet;
}

int XortJit64::do_label( int iIdx )
{
    m_pGenerator->declare_label( iIdx );
    return XORT_OK;
}

int XortJit64::ProcessPrimitive( const InstructionData& instr )
{
    switch( instr.m_bInstrCode )
    {
    // case p_addrof:  return gen_addrof( pInstrData );
    case p_load:    return gen_load( instr );
    // case p_mov:     return gen_mov( instr );
    case p_store:   return gen_store( instr );
    // case p_drop:    return gen_drop( pInstrData );
    // case p_dup:     return gen_dup( pInstrData );
    // case p_swap:    return gen_swap( pInstrData );
    // case p_or:      return gen_arith( pInstrData, &arith_or );
    // case p_xor:     return gen_arith( pInstrData, &arith_xor );
    // case p_and:     return gen_arith( pInstrData, &arith_and );
    // case p_shl:     return gen_arith( pInstrData, &arith_shl );
    // case p_shr:     return gen_arith( pInstrData, &arith_shr );
    // case p_add:     return gen_arith( pInstrData, &arith_add );
    // case p_sub:     return gen_arith( pInstrData, &arith_sub );
    // case p_mul:     return gen_arith( pInstrData, &arith_mul );
    // case p_div:     return gen_arith( pInstrData, &arith_div );
    // case p_mod:     return gen_arith( pInstrData, &arith_mod );
    // case p_inc:     return gen_unary_prim( pInstrData, &const_inc );
    // case p_dec:     return gen_unary_prim( pInstrData, &const_dec );
    // case p_neg:     return gen_unary_prim( pInstrData, &const_neg );
    // case p_not:     return gen_unary_prim( pInstrData, &const_not );
    // case p_nz:      return gen_nz( pInstrData );
    // case p_lor:     return gen_logical_op( pInstrData, &const_lor );
    // case p_land:    return gen_logical_op( pInstrData, &const_land );
    // case p_lnot:    return gen_unary_prim( pInstrData, &const_lnot );
    // case p_cmpeq:   return gen_logical_op( pInstrData, &const_cmpeq );
    // case p_cmpneq:  return gen_logical_op( pInstrData, &const_cmpneq );
    // case p_cmpgt:   return gen_logical_op( pInstrData, &const_cmpgt );
    // case p_cmplt:   return gen_logical_op( pInstrData, &const_cmplt );
    // case p_cmpge:   return gen_logical_op( pInstrData, &const_cmpge );
    // case p_cmple:   return gen_logical_op( pInstrData, &const_cmple );
    // case p_convert: return gen_convert( pInstrData );
    // case p_goto:    return gen_goto( pInstrData );
    // case p_ifz:     return gen_ifz_nz( pInstrData, &const_ifz );
    // case p_ifnz:    return gen_ifz_nz( pInstrData, &const_ifnz );
    // case p_ifeq:    return gen_if_xx( pInstrData, &const_cmpeq );
    // case p_ifneq:   return gen_if_xx( pInstrData, &const_cmpneq );
    // case p_ifleq:   return gen_if_xx( pInstrData, &const_cmple );
    // case p_ifgeq:   return gen_if_xx( pInstrData, &const_cmpge );
    // case p_ifg:     return gen_if_xx( pInstrData, &const_cmpgt );
    // case p_ifl:     return gen_if_xx( pInstrData, &const_cmplt );
    // case p_calli:   return gen_calli( pInstrData );
    // case p_call:    return gen_call( pInstrData );
    case p_ret:     return gen_ret( instr );
    // case p_new:     return gen_new( pInstrData );
    // case p_get:     return gen_get( pInstrData );
    // case p_put:     return gen_put( pInstrData );
    // case p_invoke:  return gen_invoke( pInstrData );
    // case p_gettype: return gen_gettype( pInstrData );
    // case p_enter:   return gen_enter( pInstrData );
    // case p_leave:   return gen_leave( pInstrData );
    // case p_nop:     return gen_nop( pInstrData );
    // case p_dbgbreak:return gen_dbgbreak( pInstrData );
    // case p_pack:    return gen_pack( pInstrData );
    // case p_upack:   return gen_upack( pInstrData );
    // case p_stkvar:  return XORT_OK;
    // case p_label:   return gen_label( pInstrData );
    }
    return ERR_INVALID_PRIMCODE;
}

int XortJit64::gen_load( const InstructionData& primData )
{
    int iRet = XORT_OK;
    byte bCmdType = primData.m_bTypeAddr & 0xF0;

    switch( primData.m_bTypeAddr & 0x0F )
    {
    case a_c: // ... -> val S[top] = constant
        {
            if( primData.m_Value.bType != bCmdType ) return ERR_INVALID_PRIMCODE;
            VarStorage st = m_Machine.findStorage( primData.m_Value );
            m_Operands.push( st );
            iRet = gen_load_const_operand( st, primData.m_Value );
            break;
        }
    case a_m: // ... -> val S[top] = M[#]
        {
            if( primData.m_Value.bType != t_u4 ) return ERR_INVALID_PRIMCODE;
            XortElement x = m_pGlobalVars->get( primData.m_Value._u4 );
            if( x.bType != bCmdType ) return ERR_TYPES_MISMATCH;
            qword qwAddr = m_dataPointers.get( primData.m_Value._u4 );

            VarStorage st = m_Machine.findStorage( x );
            m_Operands.push( st );
            iRet = gen_load_mem_operand( st, qwAddr, x );
            break;
        }
    case a_s: // ... -> val S[top] = S[#] this semantically gets data from local stack variable
        {
            if( primData.m_Value.bType != t_s2 ) return ERR_INVALID_PRIMCODE;
            int iIdx = xvmStackIdxToMachineIdx( primData.m_Value._s2 );
            XortElement x = m_Machine.getFromStack( iIdx );
            if( x.bType != bCmdType ) return ERR_TYPES_MISMATCH;

            VarStorage st = m_Machine.findStorage( primData.m_Value );
            m_Operands.push( st );

            iRet = gen_load_stack_operand( st, iIdx, bCmdType );
            break;
        }
    case a_indm: // ... -> val
    case a_inds: // ... -> val
        return ERR_INTERNAL; // not implemented
    case a_ti: // ... -> val
        {
/*            if( primData.m_Value.bType != t_u2 ) return ERR_INVALID_PRIMCODE;
            if( !m_Stack.canGet( m_dwStackBase ) ) return ERR_INVALID_STACK_IDX;
            XortElement& sysFr = m_Stack.get( m_dwStackBase );
            if( !(sysFr.bAccess & sys_Frame) ) return ERR_INVALID_STACK_SYSFRAME;
            if( sysFr._ti == 0 ) return ERR_THIS_PTR_NULL;
            xobject *pObj = (xobject *)sysFr._ti;
            XortElement x;
            int iRet = pObj->Get( primData.m_Value._u2, x );
            if( iRet != XORT_OK ) return iRet;
            if( x.bType != bCmdType ) return ERR_TYPES_MISMATCH;
            x.bAccess = usr_VarRW;
            m_Stack.push( x );
*/            break;
        }
    default:
        return ERR_INVALID_PRIMCODE;
    }

    return XORT_OK;
}

int XortJit64::gen_store( const InstructionData& primData )
{
    int iRet = XORT_OK;
    byte bCmdType = primData.m_bTypeAddr & 0xF0;

    switch ( primData.m_bTypeAddr & 0x0F )
    {
    case a_m:   // M[#] = S[top]
       {
            if ( primData.m_Value.bType != t_u4 ) return ERR_INVALID_PRIMCODE;
            if ( !m_Operands.canPop() ) return ERR_STACK_UNDERFLOW;
            VarStorage st = m_Operands.pop();
            
            if ( !m_pGlobalVars->canGet( primData.m_Value._u4 ) ) return ERR_INVALID_DATA_IDX;
            XortElement& m = m_pGlobalVars->get( primData.m_Value._u4 );
            if( m.bType != bCmdType ) return ERR_TYPES_MISMATCH;
            
            qword qwAddr = m_dataPointers.get( primData.m_Value._u4 );
            iRet = gen_store_operand_mem( qwAddr, st, bCmdType );
            
            m_Machine.clearStorage( st );
            break;
        }
    case a_s:    // S[#] = S[top] val -> ..., semantically, this stores the operand to local stack variable
        {
            if ( primData.m_Value.bType != t_s2 ) return ERR_INVALID_PRIMCODE;
            int iIdx = xvmStackIdxToMachineIdx( primData.m_Value._s2 );
            if ( !m_Machine.testCanAccessStk( iIdx ) ) return ERR_INVALID_STACK_IDX;

            XortElement& x = m_Machine.getFromStack( iIdx );
            if ( !m_Operands.canPop() ) return ERR_STACK_UNDERFLOW;

            VarStorage st = m_Operands.pop();
            XortElement t = m_Machine.getFromStorage( st );
            if ( x.bType != t.bType ) return ERR_TYPES_MISMATCH;

            // x = t;
            iRet = gen_store_stack_operand( iIdx, st, x.bType );

            m_Machine.clearStorage( st );
            break;
        }
   case a_indm:   // M[#] = S[top] val -> ...
        /*{
            if ( !m_Stack.canPop() ) return ERR_STACK_UNDERFLOW;
            XortElement x = m_Stack.pop();
            if ( x.bType != t_u4 ) return ERR_INVALID_TYPE;
            if ( !m_Data.canGet( x._u4 ) ) return ERR_INVALID_DATA_IDX;
            XortElement& m = m_Data.get( x._u4 );
            if ( m.bType != bCmdType ) return ERR_TYPES_MISMATCH;
            if ( !m_Stack.canPop() ) return ERR_STACK_UNDERFLOW;
            XortElement t = m_Stack.pop();
            if ( m.bType != t.bType ) return ERR_TYPES_MISMATCH;
            m = t;
            break;
        }*/
    case a_inds:   // S[#] = S[top] val -> ...
        /*{
            if ( !m_Stack.canPop() ) return ERR_STACK_UNDERFLOW;
            XortElement x = m_Stack.pop();
            if ( x.bType != t_s2 ) return ERR_INVALID_TYPE;
            int iIdx = (int)m_dwStackBase + x._s2;
            if ( !m_Stack.canGet( (dword)iIdx ) ) return ERR_INVALID_STACK_IDX;
            XortElement& s = m_Stack.get( (dword)iIdx );
            if ( s.bType != bCmdType ) return ERR_TYPES_MISMATCH;
            if ( !m_Stack.canPop() ) return ERR_STACK_UNDERFLOW;
            XortElement t = m_Stack.pop();
            if ( s.bType != t.bType ) return ERR_TYPES_MISMATCH;
            s = t;
            break;
        }*/
    case a_ti:   // T[#] = S[top]
        /*{
            if ( primData.m_Value.bType != t_u2 ) return ERR_INVALID_PRIMCODE;
            if ( !m_Stack.canGet( m_dwStackBase ) ) return ERR_INVALID_STACK_IDX;
            XortElement& sysFr = m_Stack.get( m_dwStackBase );
            if ( !( sysFr.bAccess & sys_Frame ) ) return ERR_INVALID_STACK_SYSFRAME;
            if ( sysFr._ti == 0 ) return ERR_THIS_PTR_NULL;
            xobject *pObj = (xobject *)sysFr._ti;
            if ( !m_Stack.canPop() ) return ERR_STACK_UNDERFLOW;
            XortElement x = m_Stack.pop();
            if ( x.bType != bCmdType ) return ERR_TYPES_MISMATCH;
            iRet = pObj->Put( primData.m_Value._u2, x );
            break;
        } */
    default:
        return ERR_INVALID_PRIMCODE;
    }

    return iRet;
}

int XortJit64::gen_ret( const InstructionData& primData )
{
    while( m_Machine.getStackTop() >= m_Machine.getLocalStorageEnd() ) // need to clean local variables on stack 
    {
        XortElement v = m_Machine.Pop();
        m_pGenerator->pop_GPR( RAX );

        if( v.bType == t_p )
        {
            // TODO: check for possible object and -ref accordingly
        }
    }
    gen_Epilogue();
    return XORT_OK;
}

int XortJit64::gen_load_const_operand( const VarStorage& st, const XortElement& val )
{
    int iRet = XORT_OK;
    if( st.iType == VarStorage_REG )
    {
        mov_reg_imm_Value( (Register64)st.iIndex, val );
    }
    else if( st.iType == VarStorage_STACK )
    {
        push_imm_Value( val );
    }
    else
        iRet = ERR_INTERNAL;
    
    return iRet;
}

int XortJit64::gen_load_mem_operand( const VarStorage& st, qword qwAddr, const XortElement& val )
{
    int iRet = XORT_OK;
    if( st.iType == VarStorage_REG ) // we have an empty reg that can be used...
    {
        m_pGenerator->mov_GPR_imm_qword( (Register64)st.iIndex, qwAddr ); // mov reg, addr
        deref_reg( (Register64)st.iIndex, val );
    }
    else if( st.iType == VarStorage_STACK ) // means all regs are busy, need temporary to free one
    {
        // find local storage to temporary save register
        XortElement rgVal = m_Machine.getRegValue( RAX ); // why not use rax here?
        VarStorage stTemp = m_Machine.findLocalStorage( rgVal );
        if( stTemp.iType != VarStorage_STACK )
            return ERR_INTERNAL;

        dword dwDisp = IDX2DISP( stTemp.iIndex );
        m_pGenerator->mov_qword_ptr_Base_disp_GPR( RBP, dwDisp, RAX ); // save rax: mov [rbp-loc], rax

        m_pGenerator->mov_GPR_imm_qword( RAX, qwAddr );                          // mov rax, addr
        deref_reg( RAX, val );                                                   // mov rax, qword ptr[rax]
        m_pGenerator->push_GPR( RAX );                                           // push rax

        m_pGenerator->mov_GPR_qword_ptr_Base_disp( RAX, RBP, dwDisp ); // restore:  mov rax, [rbp-loc]
        m_Machine.clearLocalStorage( stTemp );
    }
    else
        iRet = ERR_INTERNAL;

    return iRet;
}

int XortJit64::gen_store_operand_mem( QWORD qwAddrDst, VarStorage stSrc, byte bType )
{
    int iRet = XORT_OK;
    
    if( stSrc.iType == VarStorage_REG )
    {
        store_mem_reg( qwAddrDst, (Register64)stSrc.iIndex, bType );
    }
    else if( stSrc.iType == VarStorage_STACK )
    {
        store_mem_stack( qwAddrDst, stSrc.iIndex, bType );
    }
    else
        iRet = ERR_INTERNAL;

    return iRet;
}

int XortJit64::gen_load_stack_operand( VarStorage stDst, int iStkIdx, byte bType )
{
    int iRet = XORT_OK;
    
    if( stDst.iType == VarStorage_REG ) // normal case, when we have empty register
    {
        mov_reg_stack( (Register64)stDst.iIndex, iStkIdx, bType );
    }
    else if( stDst.iType == VarStorage_STACK ) // case when we do not have empty register
    {
        // find local storage to temporary save register
        XortElement rgVal = m_Machine.getRegValue( RAX ); // why not use rax here?
        VarStorage stTemp = m_Machine.findLocalStorage( rgVal );
        if( stTemp.iType != VarStorage_STACK )
            return ERR_INTERNAL;

        dword dwDisp = IDX2DISP( stTemp.iIndex );
        m_pGenerator->mov_qword_ptr_Base_disp_GPR( RBP, dwDisp, RAX ); // save rax

        mov_reg_stack( RAX, iStkIdx, bType ); // load operand to rax
        
        m_pGenerator->push_GPR( RAX ); // put rax on stack

        m_pGenerator->mov_GPR_qword_ptr_Base_disp( RAX, RBP, dwDisp ); // restore rax
        m_Machine.clearLocalStorage( stTemp );
    }
    else
        iRet = ERR_INTERNAL;

    return iRet;
}

int XortJit64::gen_store_stack_operand( int iStkIdxDst, VarStorage stSrc, byte bType )
{
    int iRet = XORT_OK;

    if( stSrc.iType == VarStorage_REG )
    {
        mov_stack_reg( iStkIdxDst, (Register64)stSrc.iIndex, bType );
    }
    else if( stSrc.iType == VarStorage_STACK )
    {
        // find local storage to temporary save register
        XortElement rgVal = m_Machine.getRegValue( RAX );
        VarStorage stTemp = m_Machine.findLocalStorage( rgVal );
        if( stTemp.iType != VarStorage_STACK )
            return ERR_INTERNAL;

        dword dwDisp = IDX2DISP( stTemp.iIndex );
        m_pGenerator->mov_qword_ptr_Base_disp_GPR( RBP, dwDisp, RAX ); // save rax

        mov_reg_stack( RAX, stSrc.iIndex, bType ); // load operand to rax

        dword dwDispDst = IDX2DISP( iStkIdxDst );
        m_pGenerator->mov_qword_ptr_Base_disp_GPR( RBP, dwDispDst, RAX ); // put rax on stack

        m_pGenerator->mov_GPR_qword_ptr_Base_disp( RAX, RBP, dwDisp ); // restore rax
        m_Machine.clearLocalStorage( stTemp );
    }
    else
        return ERR_INTERNAL;

    return iRet;
}

int XortJit64::gen_push_operand( VarStorage stSrc )
{
    int iRet = XORT_OK;

    if( stSrc.iType == VarStorage_REG )
    {
        XortElement v = m_Machine.getRegValue( (Register64)stSrc.iIndex ); // TODO: check type and +/-ref as needed
        m_pGenerator->push_GPR( (Register64)stSrc.iIndex );
    }
    else if( stSrc.iType == VarStorage_STACK )
    {
        // TODO: check if this is really needed!
        return ERR_INTERNAL;
    }
    else
        return ERR_INTERNAL;

    return iRet;
}

void XortJit64::deref_reg( Register64 rg, const XortElement& val )
{
    switch( val.bType )
    {
    case t_v: break;
    case t_s1: // sign ?
    case t_u1: m_pGenerator->deref_GPR_byte_ptr( rg ); break;
    case t_s2:
    case t_u2: m_pGenerator->deref_GPR_word_ptr( rg ); break;
    case t_s4:
    case t_u4: m_pGenerator->deref_GPR_dword_ptr( rg ); break;
    case t_s8:
    case t_u8:
    case t_r4:
    case t_r8:
    case t_p:
    default: m_pGenerator->deref_GPR_qword_ptr( rg ); break;   // treat as t_p
    }
}

void XortJit64::mov_reg_imm_Value( Register64 rg, const XortElement& val )
{
    switch( val.bType )
    {
    case t_v: break;
    case t_s1: // what about sign extension?
    case t_u1: m_pGenerator->mov_GPR_imm_byte( rg, val._u1 ); break;
    case t_s2:
    case t_u2: m_pGenerator->mov_GPR_imm_word( rg, val._u2 ); break;
    case t_s4:
    case t_u4: m_pGenerator->mov_GPR_imm_dword( rg, val._u4 ); break;
    case t_s8:
    case t_u8:
    case t_r4:
    case t_r8:
    case t_p:
    default: m_pGenerator->mov_GPR_imm_qword( rg, val._u8 ); break;   // treat as t_p
    }
}

void XortJit64::push_imm_Value( const XortElement& val )
{
    switch( val.bType )
    {
    case t_v: break;
    case t_s1: // what about sign extension?
    case t_u1: m_pGenerator->push_imm_byte( val._u1 ); break;
    case t_s2:
    case t_u2: m_pGenerator->push_imm_word( val._u2 ); break;
    case t_s4:
    case t_u4: m_pGenerator->push_imm_dword( val._u4 ); break;
    case t_s8:
    case t_u8: 
    case t_r4: 
    case t_r8: 
    case t_p : 
    default  : m_pGenerator->push_imm_qword( val._u8 ); break;   // treat as t_p
    }
}

void XortJit64::store_mem_reg( QWORD qwAddr, Register64 rg, byte bType )
{
    switch ( bType )
    {
    case t_v: break;
    case t_s1: 
    case t_u1: m_pGenerator->mov_byte_ptr_MEM_GPR( qwAddr, rg ); break;
    case t_s2:
    case t_u2: m_pGenerator->mov_word_ptr_MEM_GPR( qwAddr, rg ); break;
    case t_s4:
    case t_u4: m_pGenerator->mov_dword_ptr_MEM_GPR( qwAddr, rg ); break;
    case t_s8:
    case t_u8:
    case t_r4:
    case t_r8:
    case t_p :
    default  : m_pGenerator->mov_qword_ptr_MEM_GPR( qwAddr, rg ); break;   // treat as t_p
    }
}

void XortJit64::store_mem_stack( QWORD qwAddr, int iIdx, byte bType )
{
    switch ( bType )
    {
    case t_v: break;
    case t_s1:
    case t_u1: m_pGenerator->mov_byte_ptr_MEM_Base_disp_GPR( qwAddr, iIdx, RBP ); break;
    case t_s2:
    case t_u2: m_pGenerator->mov_word_ptr_MEM_Base_disp_GPR( qwAddr, iIdx, RBP ); break;
    case t_s4:
    case t_u4: m_pGenerator->mov_dword_ptr_MEM_Base_disp_GPR( qwAddr, iIdx, RBP ); break;
    case t_s8:
    case t_u8:
    case t_r4:
    case t_r8:
    case t_p :
    default  : m_pGenerator->mov_qword_ptr_MEM_Base_disp_GPR( qwAddr, iIdx, RBP ); break;   // treat as t_p
    }
}

void XortJit64::mov_reg_stack( Register64 rg, int iStkIdx, byte bType )
{
    DWORD dwDisp = IDX2DISP( iStkIdx );
    switch ( bType )
    {
    case t_v:  break;
    case t_s1:
    case t_u1: m_pGenerator->mov_GPR_byte_ptr_Base_disp( rg, RBP, dwDisp ); break;
    case t_s2:
    case t_u2: m_pGenerator->mov_GPR_word_ptr_Base_disp( rg, RBP, dwDisp ); break;
    case t_s4:
    case t_u4: m_pGenerator->mov_GPR_dword_ptr_Base_disp( rg, RBP, dwDisp ); break;
    case t_s8:
    case t_u8:
    case t_r4:
    case t_r8:
    case t_p :
    default  : m_pGenerator->mov_GPR_qword_ptr_Base_disp( rg, RBP, dwDisp ); break;
    }
}

void XortJit64::mov_stack_reg( int iStkIdx, Register64 rg, byte bType )
{
    DWORD dwDisp = IDX2DISP( iStkIdx );
    switch( bType )
    {
    case t_v:  break;
    case t_s1:
    case t_u1: m_pGenerator->mov_byte_ptr_Base_disp_GPR( RBP, dwDisp, rg ); break;
    case t_s2:
    case t_u2: m_pGenerator->mov_word_ptr_Base_disp_GPR( RBP, dwDisp, rg ); break;
    case t_s4:
    case t_u4: m_pGenerator->mov_dword_ptr_Base_disp_GPR( RBP, dwDisp, rg ); break;
    case t_s8:
    case t_u8:
    case t_r4:
    case t_r8:
    case t_p :
    default  : m_pGenerator->mov_qword_ptr_Base_disp_GPR( RBP, dwDisp, rg ); break;
    }
}